/*
Copyright (c) Microsoft Open Technologies, Inc.
All Rights Reserved
See License.txt in the project root for license information.
*/
package microsoft.aspnet.signalr.client.transport;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import com.google.gson.Gson;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.exceptions.InvalidDataException;
import org.java_websocket.framing.Framedata;
import org.java_websocket.handshake.ServerHandshake;
import org.java_websocket.util.Charsetfunctions;
import microsoft.aspnet.signalr.client.ConnectionBase;
import microsoft.aspnet.signalr.client.LogLevel;
import microsoft.aspnet.signalr.client.Logger;
import microsoft.aspnet.signalr.client.SignalRFuture;
import microsoft.aspnet.signalr.client.UpdateableCancellableFuture;
import microsoft.aspnet.signalr.client.http.HttpConnection;
/**
* Implements the WebsocketTransport for the Java SignalR library
* Created by stas on 07/07/14.
*/
public class WebsocketTransport extends HttpClientTransport {
private String mPrefix;
private static final Gson gson = new Gson();
WebSocketClient mWebSocketClient;
private UpdateableCancellableFuture<Void> mConnectionFuture;
public WebsocketTransport(Logger logger) {
super(logger);
}
public WebsocketTransport(Logger logger, HttpConnection httpConnection) {
super(logger, httpConnection);
}
@Override
public String getName() {
return "webSockets";
}
@Override
public boolean supportKeepAlive() {
return true;
}
@Override
public SignalRFuture<Void> start(ConnectionBase connection, ConnectionType connectionType, final DataResultCallback callback) {
final String connectionString = connectionType == ConnectionType.InitialConnection ? "connect" : "reconnect";
final String transport = getName();
final String connectionToken = connection.getConnectionToken();
final String messageId = connection.getMessageId() != null ? connection.getMessageId() : "";
final String groupsToken = connection.getGroupsToken() != null ? connection.getGroupsToken() : "";
final String connectionData = connection.getConnectionData() != null ? connection.getConnectionData() : "";
String url = null;
try {
url = connection.getUrl() + "signalr/" + connectionString + '?'
+ "connectionData=" + URLEncoder.encode(URLEncoder.encode(connectionData, "UTF-8"), "UTF-8")
+ "&connectionToken=" + URLEncoder.encode(URLEncoder.encode(connectionToken, "UTF-8"), "UTF-8")
+ "&groupsToken=" + URLEncoder.encode(groupsToken, "UTF-8")
+ "&messageId=" + URLEncoder.encode(messageId, "UTF-8")
+ "&transport=" + URLEncoder.encode(transport, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
mConnectionFuture = new UpdateableCancellableFuture<Void>(null);
URI uri;
try {
uri = new URI(url);
} catch (URISyntaxException e) {
e.printStackTrace();
mConnectionFuture.triggerError(e);
return mConnectionFuture;
}
mWebSocketClient = new WebSocketClient(uri) {
@Override
public void onOpen(ServerHandshake serverHandshake) {
mConnectionFuture.setResult(null);
}
@Override
public void onMessage(String s) {
callback.onData(s);
}
@Override
public void onClose(int i, String s, boolean b) {
mWebSocketClient.close();
}
@Override
public void onError(Exception e) {
mWebSocketClient.close();
}
@Override
public void onFragment(Framedata frame) {
try {
String decodedString = Charsetfunctions.stringUtf8(frame.getPayloadData());
if(decodedString.equals("]}")){
return;
}
if(decodedString.endsWith(":[") || null == mPrefix){
mPrefix = decodedString;
return;
}
String simpleConcatenate = mPrefix + decodedString;
if(isJSONValid(simpleConcatenate)){
onMessage(simpleConcatenate);
}else{
String extendedConcatenate = simpleConcatenate + "]}";
if (isJSONValid(extendedConcatenate)) {
onMessage(extendedConcatenate);
} else {
log("invalid json received:" + decodedString, LogLevel.Critical);
}
}
} catch (InvalidDataException e) {
e.printStackTrace();
}
}
};
mWebSocketClient.connect();
connection.closed(new Runnable() {
@Override
public void run() {
mWebSocketClient.close();
}
});
return mConnectionFuture;
}
@Override
public SignalRFuture<Void> send(ConnectionBase connection, String data, DataResultCallback callback) {
mWebSocketClient.send(data);
return new UpdateableCancellableFuture<Void>(null);
}
private boolean isJSONValid(String test){
try {
gson.fromJson(test, Object.class);
return true;
} catch(com.google.gson.JsonSyntaxException ex) {
return false;
}
}
}